use super::FudaThing;
use chrono::DateTime;
use chrono::Local;
use derive_more::Display;
use serde::Deserialize;
use serde::Serialize;
use surrealdb::sql::Thing;
use ts_rs::TS;

///
/// Todo, 如： 爬虫🦎 任务获取kline数据、获取security清单、监听tick sse
///
#[derive(Debug, Serialize, Deserialize, Default, TS, Clone)]
#[ts(export)]
pub struct Todo {
    /// Todo Id, 自动创建
    #[ts(as = "FudaThing")]
    pub id: Option<Thing>,

    ///  Todo Name
    pub name: Option<String>,

    ///  Todo Type 类型
    pub type_: Option<TodoType>,

    ///  Todo parameter
    pub parameters: Option<Vec<TodoParameter>>,

    // updatable fields
    ///  Todo Status
    pub status: Option<TodoStatus>,

    /// todo crated at创建时间
    pub created_at: Option<DateTime<Local>>,

    /// todo plan at计划开始时间
    pub plan_at: Option<DateTime<Local>>,

    /// todo start at实际开始时间
    pub started_at: Option<DateTime<Local>>,
    /// todo end at实际结束时间
    pub ended_at: Option<DateTime<Local>>,
    ///  todo execution failures log执行失败日志
    pub failures: Option<Vec<TodoFailure>>,

    /// reference
    pub reference: Option<String>,

    /// 记录更新日期 HLC
    pub last_updated: Option<String>,
}

impl Todo {
    pub fn id_from(id_parts: &[&str]) -> String {
        id_parts.join("_")
    }

    pub fn id_raw(&self) -> String {
        let id = self.id.clone().unwrap();
        id.id.to_raw()
    }

    pub fn is_done_at_today(&self) -> bool {
        match self.ended_at {
            Some(done_at) => done_at.date_naive() == Local::now().date_naive(),
            None => false,
        }
    }

    pub fn is_pending(&self) -> bool {
        match &self.status {
            Some(status) => status == &TodoStatus::Pending,
            None => false,
        }
    }

    pub fn is_failed(&self) -> bool {
        match &self.status {
            Some(status) => status == &TodoStatus::Failed,
            None => false,
        }
    }
    pub fn is_done(&self) -> bool {
        match &self.status {
            Some(status) => status == &TodoStatus::Done,
            None => false,
        }
    }

    pub fn is_now_after_plan_at(&self) -> bool {
        match &self.plan_at {
            Some(plan_at) => &Local::now() > plan_at,
            None => true,
        }
    }

    pub fn get_parameter(&self, name: &str) -> Option<String> {
        match &self.parameters {
            Some(parameters) => {
                for parameter in parameters {
                    if parameter.name == name {
                        return Some(parameter.value.clone());
                    }
                }
                None
            }
            None => None,
        }
    }
}

#[derive(Debug, Serialize, Deserialize, Default, TS, Clone)]
#[ts(export)]
pub struct TodoFailure {
    pub message: String,
    pub failure_at: DateTime<Local>,
}

#[derive(Debug, Serialize, Deserialize, Default, TS, Clone)]
#[ts(export)]
pub struct TodoParameter {
    /// parameter name
    pub name: String,
    /// parameter value
    pub value: String,
}

#[derive(Display, Debug, Serialize, Deserialize, Default, TS, Clone, PartialEq)]
#[serde(rename_all = "UPPERCASE")]
#[ts(export)]
pub enum TodoType {
    /// crawl kzz list
    CrawlKzzList,
    /// crawl etf list
    CrawlEtfList,
    /// crawl opf list
    CrawlOpfList,
    /// crawl stock list
    #[default]
    CrawlStockList,
    /// crawl index list
    CrawlIndexList,
    /// crawl klines for a kzz
    CrawlKzzKlines,
    /// crawl klines for a etf
    CrawlEtfKlines,
    /// crawl klines for a opf
    CrawlOpfValues,
    /// crawl klines for a stock
    CrawlStockKlines,
    /// crawl klines for a index
    CrawlIndexKlines,
    /// crawl tick3 for securities using SSE
    SseTick3,
    /// crawl minute1 for securities using SSE 一分钟K线数据
    SseM001Klines,
    /// sync security (KZZ,ETF,OPF,STOCK) list
    SyncSecurities,
    /// sync security klines
    SyncKlines,
}

#[derive(Display, Debug, Serialize, Deserialize, Default, TS, Clone, PartialEq)]
#[serde(rename_all = "UPPERCASE")]
#[ts(export)]
pub enum TodoStatus {
    #[default]
    /// Pending
    Pending,
    /// Fail
    Failed,
    /// Done
    Done,
}
